Splash Screen
(啟動畫面)是一個應用程式在啟動時短時間顯示的畫面,通常該畫面用於展示和強調應用程式的品牌,同時在顯示畫面同時,應用程式也會用於進行初始化操作,載入基本的環境等。通常該畫面僅顯示數秒鐘,確保使用者不會等待太長時間。一但加載完成便會跳至主畫面,讓使用者可以開始使用應用程式的功能。
Welcome Screen
(歡迎畫面) 是當使用第一次啟動應用程式時或新用戶第一次訪問應用程式時出現的畫面,用於提供簡短的介紹與導向,讓使用者可以快速的熟悉應用程式的功能。
今天我們會教各位讀者如何將兩者加入應用程式中。
請建立一個 splash_screen.dart
的檔案,並參考以下程式碼:
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
Future.delayed(const Duration(seconds: 2), () {
// 當 splash screen 完成後要導向的頁面,這裡先暫放 placeholder
return Placeholder();
});
}
@override
void dispose() {
super.dispose();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
}
@override
Widget build(buildContext context) {
return // splash screen 樣式,這邊請讀者自行設計囉~
}
我們宣告了一個 stateful widget,並於 initState
的地方使用 SystemChrome.setEnabledSystemUIMode()
方法來設定系統的 UI 模式,這邊設定為 SystemUiMode.immersive
也就是將應用程式設定為全屏模式,隱藏系統狀態欄。接著使用 Future.delayed
設定延遲兩秒的時間進入 splash screen 後要導向的頁面。
在這短暫的兩秒鐘,會暫時顯示此 widget 中 build
回傳的內容,並於兩秒鐘結束後導向應顯示的頁面。這時也就代表此 widget 的生命週期結束,調用 dispose
方法將系統 UI 顯示模式調回正常,重新顯示狀態欄。
這麼一來簡單的 splash screen 就完成拉!您也可以參考下圖來進行設計:
接著就是讓應用程式開啟時自動先進入 splash screen,請開啟 main.dart
CupertinoApp(
// 上方皆省略
home: const SplashScreen();
);
原先的內容先別急著刪掉!!原先 home 參數後面接的是 StreamBuilder
,用於根據當前是否登入而顯示不同頁面狀態。請把這些內容建立成 dummy_screen.dart
的檔案,再將該檔案取代 Placeholder()
工具,我們最後會畫個流程讓大家比較好理解這麼做的用意。
如此便可以讓你的應用程式在開啟時即先導向 splash screen 了。
在製作 welcome screen 前,請先執行下列指令來下載所需套件
flutter pub add smooth_page_indicator shared_preferences
因為通常 welcome screen 會有多於一頁的內容,因此使用該套件來顯示流暢的指示換頁動畫,如下圖:
下載完畢後,請先建立 welcome_screen_page_1.dart
表示 welcome screen 的第一頁,並參考下列程式碼:
import 'package:flutter/cupertino.dart';
class WelcomeScreenPage1 extends StatelessWidget {
const WelcomeScreenPage1({super.key});
@override
Widget build(BuildContext context) {
return const Center(child: Text('我是第一頁'));
}
}
您可以視自己需要多少 welcome screen 的 page 來建立數個檔案,並自行決定要設計的畫面內容。我會以三頁的作法作為範例。
建立完畢後請再建立一個 welcome_screen.dart
並宣告成 stateful widget,我們將用此作為外框對現在應顯示第幾頁進行切換。請參考下方程式碼:
class _WelcomeScreenState extends State<WelcomeScreen> {
final PageController _controller = PageController();
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: Stack(children: [
PageView(
controller: _controller,
onPageChanged: (value) {},
children: const [
WelcomeScreenPage1(),
WelcomeScreenPage2(),
WelcomeScreenPage3(),
],
),
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 32),
child: Container(
alignment: Alignment.bottomCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SmoothPageIndicator(
controller: _controller,
count: 3,
effect: const WormEffect(
dotHeight: 12,
dotWidth: 12,
activeDotColor: Color.fromRGBO(255, 30, 84, 1),
dotColor: CupertinoColors.systemGrey4,
)),
],
)),
)]))}
}
接下來便是將 WelcomePage
給串接起來,並看看效果。
這是一個可以將 key 與 value 存在裝置上的套件。目前為止我們的接觸到的 state 與 provider 的值都是在應用程式關閉之後就不復存在了。而 shared preferences 套件的存在意義就是為了解決這件事情,即便應用程式關閉了,但存於 shared preferences 的 key 與 value 值仍得以保存。
我們將使用此套件來存取使用者是否是第一次進入此 APP。請參考下方程式碼:
@override
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
Future.delayed(const Duration(seconds: 2), () {
Navigator.of(context)
.pushReplacement(CupertinoPageRoute(builder: (context) {
return FutureBuilder(
future: _prefs,
builder: (context, snapshot) {
if (snapshot.hasData) {
final prefs = snapshot.data as SharedPreferences;
// 檢查是否有 isFirstTime 的值
final isFirstTime = prefs.getBool('isFirstTime') ?? true;
if (isFirstTime) {
// 若為第一次使用應用程式,則導向歡迎頁面並將該值改為 false
prefs.setBool('isFirstTime', false);
return const WelcomeScreen();
} else {
return const DummyScreen();
}
} else {
return const DummyScreen();
}
});
}));
});
}
目前看起來很棒,不過通常當 welcome page 滑到最末頁的時候,需要跳出一「完成」按鈕來讓使用者導向登入頁面,因此我們可以再做些許的更動。
請在該 widget 中新增一個 _onLastPage
的 state,用於判斷是否為最後一頁。
// PageView
PageView(
contrroller: _controller,
onPageChanged: (value) {
setState(() {
// 因為頁碼總共三頁,因此當頁碼為 2 時將該值設為 true,否則為 false
_onLastPage = value == 2;
})
}
)
// 下方 Row 的地方
children: [
CupertinoButton(onPressed: () {
Navigator.of(context).pushReplacement(
CupertinoPageRoute(builder: (context) {
return const LoginScreen();
}));
}, child: const Text('跳過')),
SmoothPageIndicator(... 省略 ...),
_onLastPage ?
CupertinoButton(onPressed() { .. 跟上面跳過的 onPressed 內容一樣 .. }, child: const Text('完成'))
: CupertinoButton(onPressed: () {
_controller.nextPage(
duration: const Duration(milliseconds: 500),
curve: Cuerves.easeIn
);
}, child: const Text('下一頁')),
]
用簡單的流程圖來表示就是如此。相當容易對吧XD
今天教了大家第一個進階技巧,也就是將 Splash Screen 與 Welcome Screen 加入到應用程式當中雖然是否加入該畫面對於應用程式本身的功能沒有任何影響,但是在使用者的體驗上能夠更加分!!
並且我們使用了 shared preferences 套件來將狀態於裝置中儲存,您也可以將 dark mode 與通知的狀態儲存於 shared preferences 中,這邊就留給讀者自己練習以及把玩拉!
最後倒數四天就結束拉~好開心好開心~~
今天的參考程式碼:https://github.com/ChungHanLin/micro_news_tutorial/tree/day26/micro_news_app